package com.alibaba.dubbo.demo.consumer;

import com.google.common.collect.Maps;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class TPMonitor {

    /**
     * 统计数据缓存，存储结构：[方法名,[请求时间戳,请求耗时]]
     */
    private Map<String, ConcurrentSkipListMap<Long, Integer>> metrics = Maps.newConcurrentMap();

    private static final ScheduledExecutorService printTimer = Executors.newScheduledThreadPool(1);

    public TPMonitor() {
        // 每5秒打印一次监控数据
        printTimer.scheduleAtFixedRate(() -> {
            try {
                String timeStr = new SimpleDateFormat("HH:mm:ss").format(new Date());
                System.out.println("---------- " + timeStr + " 最近一分钟监控数据 ----------");
                printMonitorMetric(System.currentTimeMillis() - 60 * 1000L);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }, 0, 5, TimeUnit.SECONDS);
    }

    /**
     * 记录方法请求耗时
     *
     * @param requestTime 请求时间戳（毫秒）
     * @param methodName  方法名
     * @param elapsedMs   请求耗时
     */
    public void recordMethodInvokeTime(long requestTime, String methodName, int elapsedMs) {
        ConcurrentSkipListMap<Long, Integer> map = metrics.get(methodName);
        if (map == null) {
            map = new ConcurrentSkipListMap(Comparator.naturalOrder());
            metrics.put(methodName, map);
        }
        map.put(requestTime, elapsedMs);
    }

    /**
     * 打印监控数据
     *
     * @param startTimeMs 统计开始时间戳
     */
    private void printMonitorMetric(long startTimeMs) {
        metrics.entrySet().forEach(entry -> {
            String methodName = entry.getKey();
            SortedMap<Long, Integer> lastMinMetrics = entry.getValue().tailMap(startTimeMs);
            List<Integer> elapsedMsList = lastMinMetrics.values().stream().collect(Collectors.toList());
            System.out.println("[" + methodName + "] 总请求数: " + elapsedMsList.size() +
                    ", Avg(ms): " + elapsedMsList.stream().mapToInt(Integer::intValue).sum() / elapsedMsList.size() +
                    ", TP90(ms): " + percentile(elapsedMsList, 90) +
                    ", TP99(ms): " + percentile(elapsedMsList, 99));
        });
    }

    /**
     * 计算Top Percentile统计指标
     *
     * @param list
     * @param percentile
     * @return
     */
    private static long percentile(List<Integer> list, double percentile) {
        Collections.sort(list);
        int index = (int) Math.ceil(percentile / 100.0 * list.size());
        return list.get(index - 1);
    }

}
